﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Data.Sql;
using System.Data.SqlClient;
using System.IO;

namespace vAddins
{
    public class vAddinsEngine
    {
        internal static bool Initialized = false;

        internal static string DataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Vercas", "vAddins");
        internal static string AssembliesFolder = Path.Combine(DataFolder, "Assemblies");

        internal static string AppDataFolder = "";

        internal static string _appname = null;
        /// <summary>
        /// Gets or sets the name of the host application.
        /// <para>May not be changed after the engine is initialized.</para>
        /// </summary>
        public static string HostApplicationName
        {
            get
            {
                return _appname;
            }
            set
            {
                if (Initialized)
                    throw new MemberAccessException("Unable to change vAddins.vAddinsEngine.HostApplicationName after the engine is initialized!");
                else
                {
                    _appname = value;
                }
            }
        }

        internal static List<string> _mhr = new List<string>();
        /// <summary>
        /// Gets a modifiable list of paths (strings) to assemblies that must be referenced in all add-ins.
        /// <para>vAddins is added by default.</para>
        /// </summary>
        public static List<string> MandatoryHostReferences { get { return _mhr; } }

        internal static List<vAddin> _la = new List<vAddin>();
        /// <summary>
        /// Gets a list of all the loaded add-ins.
        /// </summary>
        public static List<vAddin> LoadedAddins { get { return _la; } }

        /// <summary>
        /// Gets or sets the add-ins directory.
        /// </summary>
        public static string AddinsPath { get; set; }

        public static void Initialize()
        {
            if (_appname == null)
                AppDataFolder = Path.Combine(DataFolder, Path.GetFileNameWithoutExtension(Assembly.GetCallingAssembly().Location));
            else
                AppDataFolder = Path.Combine(DataFolder, _appname);

            List<vAddin> preparedAddins = new List<vAddin>();

            foreach (var dir in Directory.GetDirectories(AddinsPath, "*", SearchOption.TopDirectoryOnly))
                if (!(Path.GetFileName(dir).StartsWith("_") || Path.GetFileName(dir).StartsWith(".")))
                {
                    var addin = new vAddin(dir); 
                    preparedAddins.Add(addin);
                }

            foreach (var a in preparedAddins)
            {
                a.Build();

                LoadedAddins.Add(a);
            }

            Initialized = true;
        }
        
        /// <summary>
        /// Queries the loaded add-ins for objects of the specified types and returns new instances.
        /// </summary>
        /// <typeparam name="T">The type of the object to look for.</typeparam>
        /// <returns>Returns an enumerable list of the objects.</returns>
        public static IEnumerable<T> Query<T>()
        {
            foreach (var a in LoadedAddins)
                foreach (var t in a.Assembly.ExtractNewObjectsOfType<T>())
                    yield return t;
        }

        /// <summary>
        /// Queries the loaded add-ins for objects of the specified types and returns new instances constructed with the specified settings.
        /// </summary>
        /// <typeparam name="T">The type of the object to look for.</typeparam>
        /// <param name="constructorTypes">An array of the object types to look for in the constructors.</param>
        /// <param name="constructorArguments">The arguments to pass to the default constructor, if found.</param>
        /// <returns>Returns an enumerable list of the objects.</returns>
        public static IEnumerable<T> Query<T>(Type[] constructorTypes, object[] constructorArguments)
        {
            foreach (var a in LoadedAddins)
                foreach (var t in a.Assembly.ExtractNewObjectsOfType<T>(constructorTypes, constructorArguments))
                    yield return t;
        }
    }
}
